package com.allwees.core.common.util.json;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.xml.sax.SAXException;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Paths;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 *
 *
 * @author yangylsky
 *
 */
@Slf4j
public class JsonXmlUtil {
	private static final String ENCODING = "UTF-8";

	/**
	 * JSON对象转的xml字符串-格式化 带空格 换行符 输出到文件可读性好
	 *
	 * @param json
	 *            JSON对象
	 * @return 漂亮的xml字符串
	 */
	public static String jsonToFormatXml(JSONObject json) {
		StringWriter formatXml = new StringWriter();
		try {
			Document document = jsonToDocument(json);
			/* 格式化xml */
			OutputFormat format = OutputFormat.createPrettyPrint();
			// 设置缩进为4个空格
			format.setIndent(" ");
			format.setIndentSize(4);
			XMLWriter writer = new XMLWriter(formatXml, format);
			writer.write(document);
		} catch(IOException e) {
			e.printStackTrace();
		}
		return formatXml.toString();
	}

	public static String jsonToXml(String json) {
		return jsonToXml(json, false);
	}

	public static String jsonToXml(String json, boolean withVersionLine) {
		try {
			StringBuffer buffer = new StringBuffer();
			if(withVersionLine) {
				buffer.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
			}
			JSONObject jObj = JSON.parseObject(json);
			jsonToXmlstr(jObj, buffer);
			return buffer.toString();
		} catch(Exception e) {
			e.printStackTrace();
			return "";
		}
	}

    /**
     * Json to xmlstr string.
     *
     * @param jObj   the j obj
     * @param buffer the buffer
     * @return the string
     */
	private static String jsonToXmlstr(JSONObject jObj, StringBuffer buffer) {
		Set<Map.Entry<String, Object>> se = jObj.entrySet();
		for (Map.Entry<String, Object> en : se) {
			if (en.getValue().getClass().getName().equals("com.alibaba.fastjson.JSONObject")) {
				buffer.append("<").append(en.getKey()).append(">");
				JSONObject jo = jObj.getJSONObject(en.getKey());
				jsonToXmlstr(jo, buffer);
				buffer.append("</").append(en.getKey()).append(">");
			} else if (en.getValue().getClass().getName().equals("com.alibaba.fastjson.JSONArray")) {
				JSONArray jarray = jObj.getJSONArray(en.getKey());
				for (int i = 0; i < jarray.size(); i++) {
					buffer.append("<").append(en.getKey()).append(">");
					JSONObject jsonobject = jarray.getJSONObject(i);
					jsonToXmlstr(jsonobject, buffer);
					buffer.append("</").append(en.getKey()).append(">");
				}
			} else if (en.getValue().getClass().getName().equals("java.lang.String")) {
				buffer.append("<").append(en.getKey()).append(">").append(en.getValue());
				buffer.append("</").append(en.getKey()).append(">");
			}
		}
		return buffer.toString();
	}

	/**
	 * JSON对象转xml字符串-不带空格换行符
	 *
	 * @param json
	 *            JSON对象
	 * @return xml字符串
	 * @throws SAXException
	 */
	public static String JsonToXml(JSONObject json) {
		return jsonToDocument(json).asXML();
	}

	/**
	 * JSON对象转Document对象
	 *
	 * @param json
	 *            JSON对象
	 * @return Document对象
	 */
	public static Document jsonToDocument(JSONObject json) {
		Document document = DocumentHelper.createDocument();
		document.setXMLEncoding(ENCODING);
		for(String rootKey : json.keySet()) {
			Element root = DocumentHelper.createElement(rootKey);
			addJsonObjectElement(json.getJSONObject(rootKey), root);
			document.add(root);
			// root对象只能有一个，多个时只读一个
			break;
		}
		return document;
	}

	/**
	 * 添加JSONArray Element
	 *
	 * @param jsonArray
	 *            传入的json数组
	 * @param nodeName
	 *            节点名称
	 * @param arrRoot
	 *            上层节点
	 */
	private static void addJsonArrayElement(JSONArray jsonArray, String nodeName, Element arrRoot) {
		for(Object aJsonArray : jsonArray) {
			JSONObject jsonObject = (JSONObject) aJsonArray;
			Element node = DocumentHelper.createElement(nodeName);
			// 继续遍历
			for(String key : jsonObject.keySet()) {
				Element element = DocumentHelper.createElement(key);
				Object child = JSON.toJSON(jsonObject.get(key));
				if(child instanceof JSONArray) {
					// 递归
					addJsonArrayElement(jsonObject.getJSONArray(key), key, node);
				} else if(child instanceof JSONObject) {
					addJsonObjectElement(jsonObject.getJSONObject(key), element);
					node.add(element);
				} else {
					element.setText(jsonObject.getString(key));
					node.add(element);
				}
			}
			arrRoot.add(node);
		}
	}

	/**
	 * 添加JSONObject Element
	 *
	 * @param json
	 *            JSON对象
	 * @param root
	 *            上层节点
	 * @return Element对象
	 */
	private static void addJsonObjectElement(JSONObject json, Element root) {
		for(String key : json.keySet()) {
			Element node = DocumentHelper.createElement(key);
			Object child = json.get(key);
			if(child instanceof JSONObject) {
				addJsonObjectElement(json.getJSONObject(key), node);
				root.add(node);
			} else if(child instanceof JSONArray) {
				addJsonArrayElement(json.getJSONArray(key), key, root);
			} else {
				node.setText(json.getString(key));
				root.add(node);
			}
		}
	}

	/**
	 * XML字符串转JSON对象
	 *
	 * @param xmlStr
	 *            xml字符串
	 * @return JSON对象
	 * @throws DocumentException
	 */
    public static JSONObject xmlToJson(String xmlStr) {
		if(StringUtils.isBlank(xmlStr)) {
			return null;
		}
		JSONObject json = new JSONObject();
		try {
			xmlStr = xmlStr.replaceAll("\\\n", "");
			byte[] xml = xmlStr.getBytes(StandardCharsets.UTF_8);
			InputStream is = new ByteArrayInputStream(xml);
			SAXBuilder sb = new SAXBuilder();
			org.jdom.Document doc = sb.build(is);
			org.jdom.Element root = doc.getRootElement();
			json.put(root.getName(), iterateElement(root));
		} catch(JDOMException | IOException e) {
			log.error("", e);
		}
		return json;
    }

    @SuppressWarnings({"rawtypes", "unchecked"})
	private static JSONObject iterateElement(org.jdom.Element element) {
        List<org.jdom.Element> node = element.getChildren();
        JSONObject obj = new JSONObject();
        List list = null;
        for (org.jdom.Element child : node) {
            list = new LinkedList();
            String text = child.getTextTrim();
            if (StringUtils.isBlank(text)) {
                if (child.getChildren().size() == 0) {
                    continue;
                }
                if (obj.containsKey(child.getName())) {
                    list = (List) obj.get(child.getName());
                }
                list.add(iterateElement(child)); //遍历child的子节点
                obj.put(child.getName(), list);
            } else {
                if (obj.containsKey(child.getName())) {
                    Object value = obj.get(child.getName());
                    try {
                        list = (List) value;
                    } catch (ClassCastException e) {
                        list.add(value);
                    }
                }
                if (child.getChildren().size() == 0) { //child无子节点时直接设置text
                    obj.put(child.getName(), text);
                } else {
                    list.add(text);
                    obj.put(child.getName(), list);
                }
            }
        }
        return obj;
    }

	/**
	 * 文件内容转换成字符串
	 *
	 * @param filePath
	 *            文件路径
	 * @return 内容字符串
	 * @throws IOException
	 */
	public static String fileToString(URL filePath) throws IOException {
		return IOUtils.toString(filePath, ENCODING);
	}

	/**
	 * 文件内容转换成字符串
	 *
	 * @param filePath
	 *            文件路径
	 * @return 内容字符串
	 * @throws IOException
	 */
	public static String fileToString(String filePath) throws IOException {
		return IOUtils.toString(Paths.get(filePath).toUri(), ENCODING);
	}

	/**
	 * 字符串输出到文件
	 *
	 * @param str
	 *            字符串内容
	 * @param filePath
	 *            文件路径
	 * @throws IOException
	 */
	public static void stringToFile(String str, String filePath) throws IOException {
		FileUtils.writeStringToFile(Paths.get(filePath).toFile(), str, ENCODING);
	}

	/**
	 * 字符串输出到文件
	 *
	 * @param str
	 *            字符串内容
	 * @param filePath
	 *            文件路径
	 * @throws IOException
	 */
	public static void stringToFile(String str, URL filePath) throws IOException {
		FileUtils.writeStringToFile(new File(filePath.getPath()), str, ENCODING);
	}
}
